2023.4.20 修正
windowの基準、ラムダを手動で決定する
算出日を手動で決定し、その日のインデックスを算出できる
Al Barsha South Fourth
パーセンタイル 35-65
λ = 0.5
https://scrapbox.io/files/644444b445e5ee001bbd07cc.png
λ = 0.9
https://scrapbox.io/files/64444a4e6f751a001b845380.png
Marsa Dubai λ=0.9
https://scrapbox.io/files/6444511fb4af09001c519438.png
λ=0.3
https://scrapbox.io/files/644454fb6dde5e001c03ec5e.png
code:python
import pandas as pd
from datetime import timedelta
import statsmodels.api as sm
from sklearn.linear_model import LinearRegression
from sklearn.feature_selection import SelectKBest, f_regression
from sklearn.feature_selection import RFE
import numpy as np
import matplotlib.pyplot as plt
import datetime
pd.set_option('display.max_columns', None)
df_sale = pd.read_csv('Transactions.csv')
import warnings
warnings.filterwarnings('ignore')
df_sale = df_sale[df_sale'property_type_en' != 'Land']
df_sale = df_sale[df_sale'property_type_en' == 'Unit']
df_sale = df_sale[df_sale'property_usage_en' == 'Residential']
df_sale = df_sale'instance_date', 'area_name_en', 'building_name_en', 'master_project_en', "meter_sale_price"
df_sale = df_sale.rename(columns={'instance_date': 'date'})
df_sale = df_sale.rename(columns={'area_name_en': 'area'})
df_sale = df_sale.rename(columns={'building_name_en': 'building'})
df_sale = df_sale.rename(columns={'master_project_en': 'project'})
# 日付列をdatetime型に変換
df_sale'date' = pd.to_datetime(df_sale'date', format='%d-%m-%Y')
df_sale.set_index('date', inplace=True)
df_sale.index = pd.to_datetime(df_sale.index)
df_sale = df_sale.sort_index()
df_sale = df_saledf_sale.index.notnull()
#全体でエリアごとの変数を求める
#パーセンタイルで抽出
p_below = df_sale'meter_sale_price'.quantile(0.35)
p_upper = df_sale'meter_sale_price'.quantile(0.65)
percentile_df_sale = df_sale[(df_sale'meter_sale_price' >= p_below) & (df_sale'meter_sale_price' <= p_upper)]
percentile_df_sale
#ここのend_dateが算出したい日
end_date = datetime.datetime(2023,2,20)
# エリアごとの最初の取引日を計算
area_first_dates = df_sale.reset_index().groupby('area')'date'.min()
# インデックスを算出したい日
calculation_date = end_date
# エリアごとの取引数をカウント
area_transactions_counts = df_sale.groupby('area')'area'.count()
# エリアごとの取引数を格納するデータフレームを初期化
df_area_tx_times = pd.DataFrame(columns='area', 'mean_transactions')
for area in area_transactions_counts.index:
# エリアごとの最初の取引日と算出したい日の差を計算
days_diff = (calculation_date - area_first_dates.locarea).days
# エリアの取引数を取得
transactions_count = area_transactions_counts.locarea
# 平均取引数を計算
mean_transactions = transactions_count / days_diff
# 結果をデータフレームに追加
df_area_tx_times = df_area_tx_times.append({'area': area,
'mean_transactions': mean_transactions},
ignore_index=True)
# エリアをインデックスに設定
df_area_tx_times = df_area_tx_times.set_index('area')
# 基準表をデータフレームとして作成
window_mapping = pd.DataFrame({
'window': 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7,
'transactions_per_day': 0.666, 0.827, 1, 1.185, 1.384, 1.6, 1.83, 2.08, 2.363, 2.66, 3, 3.368, 3.777, 4.235, 4.75, 5.333, 6, 6.769, 7.666, 8.727, 10, 11.555, 13.5, 16
})
# df_area_tx_timesのmean_transactionsに基づいてウィンドウ期間を割り当てる関数
def assign_window(mean_transactions, window_mapping):
if mean_transactions <= 0.666:
return 30
elif mean_transactions >= 16:
return 7
else:
window_row = window_mapping[window_mapping'transactions_per_day' <= mean_transactions].iloc-1
return window_row'window'
# 新しいカラムにウィンドウ期間を割り当て
df_area_tx_times'window_period' = df_area_tx_times'mean_transactions'.apply(lambda x: assign_window(x, window_mapping))
# window_periodカラムの値を四捨五入して整数に変換
df_area_tx_times'window_period' = df_area_tx_times'window_period'.round().astype(int)
#動的ウィンドウ期間の決定が完了
#エリアごとのデータフレームで、インデックス値を求める
select_area = 'Marsa Dubai'
df_spe_area = percentile_df_sale[percentile_df_sale'area' == select_area]
#算出日以前のデータフレームに変換
specified_date = end_date
df_spe_area = df_spe_areadf_spe_area.index <= specified_date
# 同じ日付のインデックスでグループ化し、meter_sale_priceの中央値を計算
area_df_median = df_spe_area.groupby(df_spe_area.index)'meter_sale_price'.median()
# 中央値を新しいデータフレームに変換
area_df_median = area_df_median.to_frame()
# area_df_medianデータフレームのインデックスをリセット
area_df_median.reset_index(inplace=True)
area_df_median.set_index('date', inplace=True)
area_df_median.index = pd.to_datetime(area_df_median.index)
area_df_median = area_df_median.sort_index()
# 新しいデータフレームのインデックスにおいて、日付を連続するように補完し、空欄の行は直前のデータを参照する。
area_df_median = area_df_median.asfreq('D', method='ffill')
# カラム meter_sale_price を線形補完
area_df_median'meter_sale_price'.interpolate(method='linear', inplace=True)
# ローリングウィンドウを適用して smoothed_price を計算
# 整数に変換されたwindow_periodカラムの値を取得
window = df_area_tx_times.locselect_area, 'window_period'
# rolling関数に渡す
area_df_median'smoothed_price' = area_df_median'meter_sale_price'.rolling(window=window, min_periods=1).median()
# 動的ウィンドウを使って移動中央値を算出完了
#減退係数を使って指数加重移動平均=>7日間の中央値を計算
#lambda_ = df_lambda.locselect_area, 'lambda'
lambda_ = 0.9
# 重みを計算
W_ti = 1 / lambda_ #タイムリー側の重み
W_si = 1 - W_ti
# 最新のタイムリーデータを取得
pt_i = area_df_median'meter_sale_price'.iloc-1
# end_dateの1日前を取得
end_date_minus_one = end_date - datetime.timedelta(days=1)
# end_date以前の最新のデータを取得
latest_data_before_end_date = area_df_median.locarea_df_median.index <= end_date_minus_one.iloc-1
p_si = latest_data_before_end_date'smoothed_price'
# 最終的なインデックスを算出
area_df_median'original_index' = (p_si * W_si) + (pt_i * W_ti)
# 直近7日間でのrolling
rolling_window = 7
area_df_median'property_index' = area_df_median'original_index'.rolling(window=rolling_window).median()
latest_date = area_df_median.index.max()
latest_row = area_df_median.loc[area_df_median.index == latest_date, 'property_index']
latest_row'date' = latest_date
latest_row'area' = select_area
latest_row = latest_row.set_index('date')
if 'new_df' not in locals():
new_df = latest_row
else:
new_df = pd.concat(new_df, latest_row, axis=0)
# インデックスが日付であることを確認
assert isinstance(new_df.index, pd.DatetimeIndex)
# 重複したインデックスを削除
new_df = new_df.loc~new_df.index.duplicated(keep='first')
# インデックスを日付でソート
new_df.sort_index(inplace=True)
# 日付を連続したものに変換し、直前のデータで補完
new_df = new_df.asfreq('D', method='ffill')
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# 表示する日付の範囲を指定
start_date = '2022-12-28'
end_date = '2023-02-20'
# グラフの大きさを設定
fig, ax = plt.subplots(figsize=(16, 8))
# データをプロット
ax.plot(new_df.index, new_df'property_index', linewidth=1, color='royalblue')
# グラフのx軸の範囲を設定
ax.set_xlim(pd.to_datetime(start_date), pd.to_datetime(end_date))
plt.ylim(0, 20000)
# グラフのタイトルとラベルを設定
ax.set_title('Property_index')
ax.set_xlabel('Date')
ax.set_ylabel('Index')
# グラフを表示
plt.show()